package net.sf.fmj.codegen;

/**
 * Generic code-generation utilities, useful for creating unit tests.
 * 
 * @author Ken Larson
 * 
 */
public class CGUtils {
	public static String toLiteral(byte[] ba) {
		if (ba == null)
			return "null";
		else {
			StringBuffer buf = new StringBuffer();
			buf.append("new byte[] {");
			for (int i = 0; i < ba.length; ++i) {
				if (i > 0)
					buf.append(", ");
				buf.append("(byte)" + (int) ba[i]);

			}
			buf.append("}");
			return buf.toString();
		}

	}

	public static String toLiteral(int[] ba) {
		if (ba == null)
			return "null";
		else {
			StringBuffer buf = new StringBuffer();
			buf.append("new int[] {");
			boolean first = true;
			for (int i = 0; i < ba.length; ++i) {
				final int b = ba[i];
				if (first)
					first = false;
				else
					buf.append(",");
				buf.append("" + (int) b);
			}
			buf.append("}");
			return buf.toString();
		}

	}

	public static String toHexLiteral(int v) {
		return "0x" + Integer.toHexString(v);
	}

	public static String toLiteral(float v) {
		return "" + v + "f";
	}

	public static String toLiteral(long v) {
		return "" + v + "L";
	}

	public static String toLiteral(int v) {
		return "" + v;
	}

	public static String toLiteral(double v) {
		return "" + v; // TODO
	}

	public static String toLiteral(String s) {
		if (s == null)
			return "null";
		return "\"" + replaceSpecialJavaStringChars(s) + "\"";
	}

	public static String replaceSpecialJavaStringChars(String raw) {
		// TODO: non-printable characters.
		if (raw == null) {
			return null;
		}

		final StringBuffer buf = new StringBuffer();

		for (int i = 0; i < raw.length(); ++i) {
			char c = raw.charAt(i);

			if (c == '\"')
				buf.append("\\\"");
			else if (c == '\'')
				buf.append("\\\'");
			else if (c == '\\')
				buf.append("\\\\");
			else if (c == '\r')
				buf.append("\\r");
			else if (c == '\n')
				buf.append("\\n");
			else if (c == '\t')
				buf.append("\\t");
			else if (c == '\f')
				buf.append("\\f");
			else if (c == '\b')
				buf.append("\\b");
			else if (c == (char) 0)
				buf.append("\\000");
			else {
				buf.append(c);
			}
		}

		return buf.toString();
	}

	public static String toNameDotClass(Class c) {
		if (c == null)
			return null;
		return toName(c) + ".class";
	}

	static String toName(Class c) {
		if (c == int.class)
			return "int";
		else if (c == boolean.class)
			return "boolean";
		else if (c == short.class)
			return "short";
		else if (c == byte.class)
			return "byte";
		else if (c == char.class)
			return "char";
		else if (c == float.class)
			return "float";
		else if (c == double.class)
			return "double";
		else if (c == long.class)
			return "long";
		else if (c == byte[].class)
			return "byte[]";
		else if (c == int[].class)
			return "int[]";
		else if (c == short[].class)
			return "short[]";
		else if (c == double[].class)
			return "double[]";
		else if (c == float[].class)
			return "float[]";
		else if (c == long[].class)
			return "long[]";
		else if (c == boolean[].class)
			return "boolean[]";
		else if (c == char[].class)
			return "char[]";
		else if (c.isArray())
			return "" + toName(c.getComponentType()) + "[]";
		else
			return "" + c.getName() + "";
	}

	/**
	 * Dump to string using a debugger-like format - both hex and ascii.
	 */
	public static String dump(byte[] bytes, int offset, int byteslen) {
		final StringBuffer b = new StringBuffer();

		final int width = 32;
		int len = width;
		while (offset < byteslen) {
			int remainder = 0;

			if (offset + len > byteslen) {
				len = byteslen - offset;
				remainder = width - len;
			}
			b.append(byteArrayToHexString(bytes, len, offset));
			for (int i = 0; i < remainder; ++i) {
				b.append("  ");
			}
			b.append(" | ");
			for (int i = 0; i < len; ++i) {
				byte c = bytes[offset + i];
				if (c >= ' ' && c <= '~') {
					b.append((char) c);
				} else
					b.append('.');
			}

			b.append('\n');

			offset += len;

		}
		return b.toString();
	}

	public static String byteArrayToHexString(byte[] array) {
		return byteArrayToHexString(array, array.length);
	}

	public static String byteArrayToHexString(byte[] array, int len) {
		return byteArrayToHexString(array, len, 0);

	}

	public static String byteArrayToHexString(byte[] array, int len, int offset) {
		StringBuffer b = new StringBuffer();
		for (int i = 0; i < len; ++i) {
			String byteStr = Integer.toHexString(uByteToInt(array[offset + i]));
			if (byteStr.length() == 1)
				byteStr = "0" + byteStr;
			b.append(byteStr);
		}
		return b.toString();
	}

	private static final int MAX_BYTE_PLUS1 = 256;

	// because java does not support unsigned bytes, we can use this function to
	// treat a byte as unsigned.
	// TODO: duplicated in UnsignedUtils
	public static int uByteToInt(byte b) {
		if (b >= 0)
			return b;
		else
			return MAX_BYTE_PLUS1 + b;
	}

	public static byte[] hexStringToByteArray(String s) {
		byte[] array = new byte[s.length() / 2];
		for (int i = 0; i < array.length; ++i) {
			array[i] = hexStringToByte(s.substring(i * 2, i * 2 + 2));
		}
		return array;
	}

	public static byte hexStringToByte(String s) {
		return (byte) Integer.parseInt(s, RADIX_16);
	}

	private static final int RADIX_16 = 16;
}
